using System;
using System.Windows;
using System.Windows.Input;

namespace Byond.Vst.WpfSendKeys
{
	public class SendKeys
	{
		//public static void Send(UIElement element, string text)
		//{
		//    var sequence = SendKeysParser.Parse(text);
		//    foreach (var keyPressInfo in sequence)
		//        Send(element, keyPressInfo.Key, keyPressInfo.Modifiers, true, true);
		//}

		public static void Send(UIElement element, Key key, ModifierKeys modifiers, bool down, bool up)
		{
			var keyboardDevice = InputManager.Current.PrimaryKeyboardDevice;
			if (modifiers != ModifierKeys.None)
			{
				var mockKeyboardDevice = MockKeyboardDevice.Instance;
				mockKeyboardDevice.Modifiers = modifiers;
				keyboardDevice = mockKeyboardDevice;
			}
			if (down)
				RaiseKeyDownEvent(element, key, modifiers, keyboardDevice);
			if (up)
				RaiseKeyUpEvent(element, key, keyboardDevice);
		}

		public static void SendInput(UIElement element, string text)
		{
			var inputManager = InputManager.Current;
			InputDevice inputDevice = inputManager.PrimaryKeyboardDevice;
			var composition = new TextComposition(inputManager, element, text);
			var args = new TextCompositionEventArgs(inputDevice, composition);
			args.RoutedEvent = UIElement.PreviewTextInputEvent;
			element.RaiseEvent(args);
			args.RoutedEvent = UIElement.TextInputEvent;
			element.RaiseEvent(args);
		}

		private static void RaiseKeyDownEvent(UIElement element, Key key, ModifierKeys modifiers, KeyboardDevice keyboardDevice)
		{
			var presentationSource = PresentationSource.FromVisual(element);
			var timestamp = Environment.TickCount;
			var args = new KeyEventArgs(keyboardDevice, presentationSource, timestamp, key);

			// 1) PreviewKeyDown
			args.RoutedEvent = Keyboard.PreviewKeyDownEvent;
			element.RaiseEvent(args);

			// 2) KeyDown
			args.RoutedEvent = Keyboard.KeyDownEvent;
			element.RaiseEvent(args);

			// 3) TextInput
			SendInputIfNecessary(element, key, modifiers);
		}

		private static void RaiseKeyUpEvent(UIElement element, Key key, KeyboardDevice keyboardDevice)
		{
			var presentationSource = PresentationSource.FromVisual(element);
			var timestamp = Environment.TickCount;
			var args = new KeyEventArgs(keyboardDevice, presentationSource, timestamp, key);

			// 4) PreviewKeyUp
			args.RoutedEvent = Keyboard.PreviewKeyUpEvent;
			element.RaiseEvent(args);

			// 5) KeyUp
			args.RoutedEvent = Keyboard.KeyUpEvent;
			element.RaiseEvent(args);
		}

		private static void SendInputIfNecessary(UIElement element, Key key, ModifierKeys modifiers)
		{
			if (modifiers == ModifierKeys.Control 
				|| modifiers == ModifierKeys.Alt 
				|| modifiers == (ModifierKeys.Shift | ModifierKeys.Control)
				|| modifiers == (ModifierKeys.Shift | ModifierKeys.Alt)
				)
			{
				return;
			}

			var input = "";

			input = KeyboardLayout.Instance.GetInputForGesture(new KeyPressInfo(key, modifiers));
			if (input == "")
				input = GetInputFromKey(key);

			if (string.IsNullOrEmpty(input))
				return;

			if (modifiers == ModifierKeys.Shift)
				input = input.ToUpperInvariant();
			else
				input = input.ToLowerInvariant();

			SendInput(element, input);
		}

		private static string GetInputFromKey(Key key)
		{
			switch (key)
			{
				case Key.Left:
				case Key.Up:
				case Key.Right:
				case Key.Down:
				case Key.Home:
				case Key.End:
				case Key.Insert:
				case Key.Delete:
				case Key.PageUp:
				case Key.PageDown:
				case Key.Back:
				case Key.Escape:
				case Key.Enter:
				case Key.Tab:
				case Key.Space:
				case Key.LeftShift:
				case Key.RightShift:
				case Key.LeftAlt:
				case Key.RightAlt:
				case Key.LeftCtrl:
				case Key.RightCtrl:
				case Key.F1:
				case Key.F2:
				case Key.F3:
				case Key.F4:
				case Key.F5:
				case Key.F6:
				case Key.F7:
				case Key.F8:
				case Key.F9:
				case Key.F10:
				case Key.F11:
				case Key.F12:
				case Key.F13:
				case Key.F14:
				case Key.F15:
				case Key.F16:
				case Key.F17:
				case Key.F18:
				case Key.F19:
				case Key.F20:
				case Key.F21:
				case Key.F22:
				case Key.F23:
				case Key.F24:
					return "";
			}
			return new KeyConverter().ConvertToInvariantString(key);
		}
	}
}